package com.pkk.cloud.support.oauth2.config.integration;

import com.pkk.cloud.support.oauth2.common.utils.ClientUtil;
import com.pkk.cloud.support.oauth2.config.integration.authenticator.IntegrationAuthenticator;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Map;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.OrRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.GenericFilterBean;

/**
 * @description: 定义拦截器拦截/oauth/token的get与post的请求并进行特殊处理
 * @author: peikunkun
 * @create: 2020-03-18 11:45
 **/
@Component
public class IntegrationAuthenticationFilter extends GenericFilterBean implements ApplicationContextAware {

  //鉴权类型[如dingding]
  private static final String AUTH_TYPE_PARM_NAME = "auth_type";
  //授权token的地址
  private static final String OAUTH_TOKEN_URL = "/oauth/token";
  private ApplicationContext applicationContext;

  //身份验证器
  private Collection<IntegrationAuthenticator> authenticators;


  //请求匹配器[只求post，get的OAUTH_TOKEN_URL]
  private RequestMatcher requestMatcher;

  public IntegrationAuthenticationFilter() {
    this.requestMatcher = new OrRequestMatcher(
        new AntPathRequestMatcher(OAUTH_TOKEN_URL, "GET"),
        new AntPathRequestMatcher(OAUTH_TOKEN_URL, "POST")
    );
  }


  /**
   * @Description: 执行过滤器
   * @Param: servletRequest
   * @Param servletResponse
   * @Param filterChain
   * @return: void
   * @Author: peikunkun
   * @Date: 2020/3/18 0018 下午 12:32
   */
  @Override
  public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
      throws IOException, ServletException {

    HttpServletRequest request = (HttpServletRequest) servletRequest;
    HttpServletResponse response = (HttpServletResponse) servletResponse;

    //获取tokend的过滤操作
    if (requestMatcher.matches(request)) {
      //设置集成登录信息
      IntegrationAuthentication integrationAuthentication = new IntegrationAuthentication();
      integrationAuthentication.setAuthType(request.getParameter(AUTH_TYPE_PARM_NAME));
      integrationAuthentication.setAuthParameters(request.getParameterMap());
      integrationAuthentication.setClientId(ClientUtil.getClientId((HttpServletRequest) servletRequest));
      IntegrationAuthenticationContext.set(integrationAuthentication);

      try {
        //预处理
        this.prepare(integrationAuthentication);

        filterChain.doFilter(request, response);

        //后置处理
        this.complete(integrationAuthentication);
      } catch (Exception e) {
        logger.error(e.getMessage(), e);
      } finally {
        IntegrationAuthenticationContext.clear();
      }
    } else {
      filterChain.doFilter(request, response);
    }
  }

  /**
   * 进行预处理
   *
   * @param integrationAuthentication
   */
  private void prepare(IntegrationAuthentication integrationAuthentication) {
    //延迟加载认证器
    if (this.authenticators == null) {
      synchronized (this) {
        //获取认证器【自定义不同的认证器】
        Map<String, IntegrationAuthenticator> integrationAuthenticatorMap = applicationContext
            .getBeansOfType(IntegrationAuthenticator.class);
        if (integrationAuthenticatorMap != null) {
          this.authenticators = integrationAuthenticatorMap.values();
        }
      }
    }

    if (this.authenticators == null) {
      this.authenticators = new ArrayList<>();
    }

    for (IntegrationAuthenticator authenticator : authenticators) {
      //判断是否支持认证类型
      if (authenticator.support(integrationAuthentication)) {
        //进行认证前置操作处理
        authenticator.prepare(integrationAuthentication);
      }
    }
  }

  /**
   * 后置处理
   *
   * @param integrationAuthentication
   */
  private void complete(IntegrationAuthentication integrationAuthentication) {
    //获取认证器的认证信息
    for (IntegrationAuthenticator authenticator : authenticators) {
      //进行认证的后续处理类型
      if (authenticator.support(integrationAuthentication)) {
        authenticator.complete(integrationAuthentication);
      }
    }
  }

  @Override
  public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
    this.applicationContext = applicationContext;
  }

}
